package com.xxx.ratelimiter;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 计数器限流算法：
 * 		在一个计数时间窗口内，使用AtomicInteger原子计数器统计请求次数，如果请求次数达到限流阈值，触发限流；如果超过时间窗口，重置计数器。
 * 优点：
 * 		实现简单，限流准确。
 * 缺点：
 * 		临界值问题：假定限流为每s最多100个请求，如果有一个用户，他在0:59时，瞬间发送了100个请求（此时不会触发限流），并且1:00又瞬间发送了100个请求，那么其实这个用户在1秒里面，瞬间发送了200个请求。
 * 		可以瞬间超过我们的速率限制。用户有可能通过这个漏洞，瞬间压垮我们的应用。
 */
public class CounterLimiter {

	public static AtomicInteger reqCount = new AtomicInteger(0); // api请求计数器（AtomicInteger保证操作原子性）
	private static long timestamp = System.currentTimeMillis(); // 系统时间戳
	private static final long INTERVAL = 1000 *10; // 计数时间窗口：10s
	private static final int LIMIT = 10; // 计数时间窗口内最大请求数量
	
	/**
	 * 判断是否超过限流阀值
	 * @return true：未被限流，false：被限流
	 */
	public static boolean checkLimit() {
		long now = System.currentTimeMillis();
		if(now < timestamp + INTERVAL) { // 未超过计数时间窗口
			int cnt = reqCount.getAndAdd(1); // 原子操作：请求数+1
			System.out.println("1-----"+cnt);
			if(cnt<LIMIT) { // 未超过限流阀值
				System.out.println("1-----未被限流");
				return true;
			} else { // 达到限流阀值
				System.out.println("1-----被限流!!!");
				return false;
			}
		} else { // 超过计数时间窗口
			// 重置限流器属性：时间窗口起始时间戳、请求次数计数器
			timestamp = System.currentTimeMillis();
			reqCount = new AtomicInteger(0);  // 重置计数器
			int cnt = reqCount.getAndAdd(1); // 原子操作：请求数+1
			System.out.println("2-----"+cnt);
			return true;
		}
	}

}
